//
// const.cs: Constant declarations.
//
// Author:
//   Miguel de Icaza (miguel@ximian.com)
//   Marek Safar (marek.safar@seznam.cz)
//
// Copyright 2001-2003 Ximian, Inc.
// Copyright 2003-2008 Novell, Inc.
//

#if STATIC
using IKVM.Reflection;
#else
using System.Reflection;
#endif

namespace Teal.Compiler {

	public class ConstSpec : FieldReference
	{
		Expression value;

		public ConstSpec (TypeReference declaringType, IMemberDefinition definition, TypeReference memberType, FieldInfo fi, Modifiers mod, Expression value)
            : base()
		{
			this.value = value;
		}

		//
		// For compiled constants we have to resolve the value as there could be constant dependecies. This
		// is needed for imported constants too to get the right context type
		//
		public Constant GetConstant (ResolveContext rc)
		{
			if (value.eclass != ExprClass.Value)
				value = value.Resolve (rc);

			return (Constant) value;
		}
	}

	public class ConstInitializer : ShimExpression
	{
		bool in_transit;
		readonly FieldDefinition field;

		public ConstInitializer (FieldDefinition field, Expression value, Location2 loc)
			: base (value)
		{
			this.loc = loc;
			this.field = field;
		}

		public string Name { get; set; }

		protected override Expression DoResolve (ResolveContext unused)
		{
			if (type2 != null)
				return expr;

			var opt = ResolveContext.Options.ConstantScope;
			if (field.containerType is EnumDefinition)
				opt |= ResolveContext.Options.EnumScope;

			//
			// Use a context in which the constant was declared and
			// not the one in which is referenced
			//
			var rc = new ResolveContext (field, opt);
			expr = DoResolveInitializer (rc);
			type2 = expr.Type;

			return expr;
		}

		protected virtual Expression DoResolveInitializer (ResolveContext rc)
		{
			if (in_transit) {
				Compiler.Report.Error (110, expr.Location,
					"The evaluation of the constant value for `{0}' involves a circular definition",
					GetSignatureForError ());

				expr = null;
			} else {
				in_transit = true;
				expr = expr.Resolve (rc);
			}

			in_transit = false;

			if (expr != null) {
				Constant c = expr as Constant;
				if (c != null)
					c = field.ConvertInitializer (rc, c);

				if (c == null) {
					if (TypeReference.IsReferenceType (field.MemberType2))
						Error_ConstantCanBeInitializedWithNullOnly (rc, field.MemberType2, expr.Location, GetSignatureForError ());
					else if (!(expr is Constant))
						Error_ExpressionMustBeConstant (rc, expr.Location, GetSignatureForError ());
					else
						expr.Error_ValueCannotBeConverted (rc, field.MemberType2, false);
				}

				expr = c;
			}

			if (expr == null) {
				expr = NewExpression.Constantify (field.MemberType2, Location);
				if (expr == null)
					expr = Constant.CreateConstantFromValue (field.MemberType2, null, Location);
				expr = expr.Resolve (rc);
			}

			return expr;
		}

		public override string GetSignatureForError ()
		{
			if (Name == null)
				return field.GetSignatureForError ();

			return field.containerType.GetSignatureForError () + "." + Name;
		}
	}
}
